﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LED_Editor.Model;


namespace LED_Editor.ControllerProgramming {
	public class BasicControllerProgrammingStrategy : ControllerProgrammingStrategyBase {
		public static readonly byte FlashIsFull = Convert.ToByte('f');
		public static readonly byte NotifySendFrame = Convert.ToByte('f');
		public static readonly byte EraseFlash = Convert.ToByte('e');
		public static readonly byte EnterProgramming = Convert.ToByte('p');
		public static readonly byte Ready = Convert.ToByte('r');
		public static readonly byte AllFramesSended = Convert.ToByte('E');

		public static readonly byte IsEmpty = Convert.ToByte('e');
		public static readonly byte IsFull = Convert.ToByte('F');
		public static readonly byte IsReady = Convert.ToByte('R');

		public static readonly byte IsYes = Convert.ToByte('1');
		public static readonly byte IsNo = Convert.ToByte('0');




		public BasicControllerProgrammingStrategy() {
		}



		public override bool IsReadyForProgramming {
			get {
				SendQuery(Ready);
				byte answer = (byte)WaitAnswer();
				return answer == IsYes;
			}
		}



		public bool TryOpenConnection(out Exception error) {
			try {
				OpenConnection();
				error = null;
				return true;
			} catch (Exception ex) {
				error = ex;
				return false;
			}
		}

		public override void SendQuery(object query) {
			if (query is byte) {
				SendQuery((byte)query);
			} else if (query is byte[]) {
				SendQuery((byte[])query);
			}
		}

		public void SendQuery(byte query) {
			Log("Запрос: " + ControllerHelper.GetQueryDescription(query));
		}
		public override void SendQuery(params byte[] query) {
			StringBuilder sb = new StringBuilder();
			sb.Append("Запрос: ");
			foreach (byte eachByte in query) {
				sb.Append(ControllerHelper.GetQueryDescription(eachByte));
			}
			Log(sb.ToString());
		}

		public override void SendFrame(FrameStruct frame) {
			Log("Отправка кадра: " + frame.ToString());
		}

		public override object WaitAnswer() {
			throw new NotImplementedException();
		}


		public void ReportError(string errorText) {
			throw new ControllerException(string.Format("Состояние: {0}, Адрес: {1}. {2}", State, Address, errorText));
		}

		public void ReportInvalidAnswer(byte answer) {
			ReportError(string.Format("Неверный ответ контроллера: {0}", Convert.ToChar(answer)));
		}


		public virtual ControllerState State { get; private set; }

		public override void Programme(IEnumerable<FrameStruct> frames) {
			IEnumerator<FrameStruct> it = frames.GetEnumerator();
			byte answer;
			State = ControllerState.Begin;


			try {
				OpenConnection();

				if (!IsReadyForProgramming) {
					ReportError("Контроллер не готов к программированию.");
				}

				while (true) {
					switch (State) {
						case ControllerState.CheckIsFull:
							SendQuery(FlashIsFull);
							answer = (byte)WaitAnswer();

							if (answer == IsYes) {
								State = ControllerState.Begin;
							} else if (answer == IsNo) {
								State = ControllerState.Ready;
							} else {
								ReportInvalidAnswer(answer);
							}
							break;
						case ControllerState.Begin:
							SendQuery(FlashIsFull);
							answer = (byte)WaitAnswer();

							if (answer == IsYes) {
								State = ControllerState.EraseFlash;
							} else if (answer == IsNo) {
								State = ControllerState.Ready;
							} else {
								ReportInvalidAnswer(answer);
							}
							break;
						case ControllerState.EraseFlash:
							SendQuery(EraseFlash);
							answer = (byte)WaitAnswer();

							if (answer == IsReady) {
								State = ControllerState.Begin;
							} else {
								ReportInvalidAnswer(answer);
							}
							break;

						case ControllerState.Ready:
							SendQuery(EnterProgramming);
							answer = (byte)WaitAnswer();

							if (answer == IsEmpty) {
								State = ControllerState.Programming;
							} else if (answer == IsFull) {
								State = ControllerState.Begin;
							} else {
								ReportInvalidAnswer(answer);
							}

							break;
						case ControllerState.Programming:

							if (it.MoveNext()) {
								SendQuery(NotifySendFrame);
								answer = (byte)WaitAnswer();

								if (answer == IsReady) {
									State = ControllerState.WaitFrame;
								} else {
									ReportInvalidAnswer(answer);
								}

							} else {
								SendQuery(AllFramesSended);
								answer = (byte)WaitAnswer();
								if (answer == IsReady) {
									State = ControllerState.End;
								} else {
									ReportInvalidAnswer(answer);
								}
							}


							break;
						case ControllerState.WaitFrame:
							SendFrame(it.Current);
							answer = (byte)WaitAnswer();

							if (answer == IsReady) {
								State = ControllerState.Programming;
							} else {
								ReportInvalidAnswer(answer);
							}


							break;
						case ControllerState.End:

							return;
						default:
							throw new NotSupportedException("Состояние контроллера " + State + " не поддерживается");
					}
				}
			} catch (ControllerException e) {
				State = ControllerState.End;
				Log("ОШИБКА: {0}. Подробнее: {1}", e.Message, e.InnerException == null ? "нет информации" : e.InnerException.Message);
				throw e;
			} finally {
				CloseConnection();
			}
		}

		#region IControllerProgrammingStrategy Members


		#endregion

		#region IControllerProgrammingStrategy Members


		public virtual bool IsOpen {
			get { return false; }
		}


		public virtual int Timeout {
			get { return 0; }
			set { }
		}

		#endregion
	}

}
